`include "top.vh"

module exe_stage(
    input                          clk           ,
    input                          reset         ,
    //allowin
    input                          ms_allowin    ,
    output                         es_allowin    ,
    //from ds
    input                          ds_to_es_valid,
    input  [`ds_2_es_bus_wid - 1:0] ds_to_es_bus  ,
    //to ms
    output                         es_to_ms_valid,
    output [`es_2_ms_bus_wid - 1:0] es_to_ms_bus  ,
    //to id
    output [38:0] es_to_id_bus  ,
    // data sram interface
    output        data_sram_req,
    output [ 3:0] data_sram_wstrb,
    output        data_sram_wr,
    output [31:0] data_sram_addr,
    output [31:0] data_sram_wdata,
    output [1 :0] data_sram_size,
    input         data_sram_addr_ok,
    //csr data block
    output read_csr,
    //ex
    input  ms_ex,
    input  ms_ertn_flush,
    input  ws_ex,
    input  ws_ertn_flush
);

wire es_mul_w;
wire es_mulh_w;
wire es_mulh_wu;
wire es_div_w;
wire es_div_wu;
wire es_mod_w;
wire es_mod_wu;

wire [63:0] unsigned_prod, signed_prod;

wire signed_output_valid;
wire unsigned_output_valid;

wire signed_input_valid;
wire signed_s_axis_divisor_tready;
wire signed_s_axis_dividend_tready;
wire [63:0] signed_output_data;
reg [1:0] signed_status;
reg [1:0] signed_next_status;

wire unsigned_input_valid;
wire unsigned_s_axis_divisor_tready;
wire unsigned_s_axis_dividend_tready;
wire [63:0] unsigned_output_data;
reg [1:0] unsigned_status;
reg [1:0] unsigned_next_status;

reg         es_valid      ;
wire        es_ready_go   ;

wire [11:0] es_alu_op ;
wire [ 3:0] es_mem_op;
wire        es_gr_we;
wire        es_mem_we;
wire [ 4:0] es_dest;
wire [31:0] es_rj_value;
wire [31:0] es_rkd_value;
wire [31:0] es_pc;
wire [31:0] es_alu_src1;
wire [31:0] es_alu_src2;
wire        es_rf_we;
wire [31:0] es_alu_result;
wire [31:0] alu_result;
wire        es_res_from_mem;
wire [ 2:0] es_ld_op;
wire [31:0] vaddr;
wire [ 1:0] es_vaddr2;
wire [31:0] es_st_data;
wire        sl_w, sl_h, sl_b;

wire [`CSR_SIG_wid-1:0] csr_signal;
wire ds_ex, ds_ertn;
wire es_ex, es_ertn;
wire ex_ale;
wire [`EX_SIG_wid-1:0] ds_ex_signal, es_ex_signal;

reg  [63:0] timer_cnt;
wire es_rdcntvl_w, es_rdcntvh_w;

//reg         es_req;
wire        es_req;
wire [ 3:0] data_sram_we;
wire        data_sram_en;
wire        data_sram_hand;

always @(posedge clk ) begin
    if (reset)
        timer_cnt <= 64'b0;
    else
        timer_cnt <= timer_cnt - 1'b1;
end

assign ex_ale =    ((es_mem_op[1:0] == 2'b10) & (es_vaddr2[0] != 1'b0)) |
                    ((es_mem_op[1:0] == 2'b11) & (es_vaddr2 != 1'b0));
assign es_ex = ds_ex || ex_ale;
assign es_ex_signal =   ds_ex   ? ds_ex_signal :
                        ex_ale ? `EX_SIG_ALE :
                        4'h0;
assign es_ertn = ds_ertn;

assign csr_re = csr_signal[`CSR_SIG_RE];
assign read_csr = csr_re && es_valid;

reg [`ds_2_es_bus_wid - 1:0] ds_to_es_bus_reg;

always@(posedge clk) begin
    if (es_allowin) begin
        ds_to_es_bus_reg <= ds_to_es_bus;
    end
end

assign  {
            es_rdcntvl_w,
            es_rdcntvh_w,
            csr_signal,     //exp12: add 81 bit
            ds_ex,             //1
            ds_ex_signal,   //16
            ds_ertn,           //1
            
            es_alu_op,      //12 
            es_mem_op,      //4
            es_gr_we,       //1
            es_dest,        //5
			es_rkd_value,   //32
            es_pc,          //32
            es_alu_src1,    //32
            es_alu_src2,    //32
            es_mul_w,       //1
            es_mulh_w,      //1
            es_mulh_wu,     //1
            es_div_w,       //1
            es_div_wu,      //1
            es_mod_w,       //1
            es_mod_wu       //1
} = ds_to_es_bus_reg;

assign es_res_from_mem = es_mem_op[3] && !es_ex;        //如果发生取地址异常，复用alu的数据通路，不使用mem阶返回的内存数据
assign vaddr = alu_result;
assign es_vaddr2 = vaddr[1:0];

assign es_to_ms_bus = {
                        csr_signal,     //81
                        es_ex,             //1
                        es_ex_signal,   //16
                        es_ertn,           //1
                        
                        es_ld_op,       //3
                        es_vaddr2,      //2
                        es_alu_result,  //32
                        es_gr_we,       //1
                        es_dest,        //5
                        es_pc           //32
};

assign es_to_id_bus = {
                        es_res_from_mem,//1
                        es_alu_result,  //32
                        es_rf_we,       //1
                        es_dest         //5

};

assign es_ld_op = es_mem_op[2:0];

assign es_rf_we = es_gr_we & es_valid;
assign es_ready_go    = data_sram_req?data_sram_addr_ok:
                    (!(es_div_w || es_mod_w || es_div_wu || es_mod_wu)) || ((es_div_w || es_mod_w) && signed_output_valid) || ((es_div_wu || es_mod_wu) && unsigned_output_valid);
assign es_allowin = (~es_valid)|(es_ready_go & ms_allowin);
assign es_to_ms_valid = es_valid & es_ready_go;

always @(posedge clk) begin
    if (reset) begin     
        es_valid <= 1'b0;
    end
    else if (ws_ex || ws_ertn_flush) 
        es_valid <= 1'b0;
    else if (es_allowin) begin 
        es_valid <= ds_to_es_valid;
    end
end


alu u_alu(
    .alu_op     (es_alu_op    ),
    .alu_src1   (es_alu_src1  ),
    .alu_src2   (es_alu_src2  ),
    .alu_result (alu_result)
    );

assign unsigned_prod = es_alu_src1 * es_alu_src2;
assign signed_prod = $signed(es_alu_src1) * $signed(es_alu_src2);

always @(posedge clk) begin
    if (reset) begin     
        signed_status <= 2'd0;
    end
    else begin 
        signed_status <= signed_next_status;
    end
end

always @(*) begin
	case(signed_status)
		2'd0://waiting inst
			signed_next_status = (es_div_w | es_mod_w) & es_valid ? 2'd1 : 2'd0;
		2'd1://waiting input
			signed_next_status = signed_s_axis_divisor_tready & signed_s_axis_dividend_tready ? 2'd2 : 2'd1;
		2'd2://waiting output
			signed_next_status = signed_output_valid ? 2'd0 : 2'd2;
		default:
			signed_next_status = 2'd3;
	endcase
end
	
assign signed_input_valid = signed_status == 2'd1;

mydiv u_mydiv(
	.aclk (clk),
	.s_axis_divisor_tvalid (signed_input_valid),
	.s_axis_divisor_tready (signed_s_axis_divisor_tready),
	.s_axis_divisor_tdata (es_alu_src2),
	.s_axis_dividend_tvalid (signed_input_valid),
	.s_axis_dividend_tready (signed_s_axis_dividend_tready),
	.s_axis_dividend_tdata (es_alu_src1),
	.m_axis_dout_tvalid (signed_output_valid),
	.m_axis_dout_tdata (signed_output_data)
	);

always @(posedge clk) begin
    if (reset) begin     
        unsigned_status <= 2'd0;
    end
    else begin 
        unsigned_status <= unsigned_next_status;
    end
end

always @(*) begin
	case(unsigned_status)
		2'd0://waiting inst
			unsigned_next_status = (es_div_wu | es_mod_wu) & es_valid ? 2'd1 : 2'd0;
		2'd1://waiting input
			unsigned_next_status = unsigned_s_axis_divisor_tready & unsigned_s_axis_dividend_tready ? 2'd2 : 2'd1;
		2'd2://waiting output
			unsigned_next_status = unsigned_output_valid ? 2'd0 : 2'd2;
		default:
			unsigned_next_status = 2'd3;
	endcase
end
	
assign unsigned_input_valid = unsigned_status == 2'd1;

mydiv_un u_mydiv_un(
	.aclk (clk),
	.s_axis_divisor_tvalid (unsigned_input_valid),
	.s_axis_divisor_tready (unsigned_s_axis_divisor_tready),
	.s_axis_divisor_tdata (es_alu_src2),
	.s_axis_dividend_tvalid (unsigned_input_valid),
	.s_axis_dividend_tready (unsigned_s_axis_dividend_tready),
	.s_axis_dividend_tdata (es_alu_src1),
	.m_axis_dout_tvalid (unsigned_output_valid),
	.m_axis_dout_tdata (unsigned_output_data)
	);

assign es_alu_result = es_mul_w ? unsigned_prod[31:0] :
                      es_mulh_w ? signed_prod[63:32] :
                      es_mulh_wu ? unsigned_prod[63:32] :
                      es_div_w ? signed_output_data[63:32] :
                      es_mod_w ? signed_output_data[31:0] :
                      es_div_wu ? unsigned_output_data[63:32] :
                      es_mod_wu ? unsigned_output_data[31:0] :
                      es_rdcntvl_w ? timer_cnt[31:0] :
                      es_rdcntvh_w ? timer_cnt[63:32] :
                      ex_ale ? vaddr :
                      alu_result;             //复用alu的数据通路传递异常数据内存地址

assign sl_w = es_mem_op == 2'b11;
assign sl_h = es_mem_op == 2'b10;
assign sl_b = es_mem_op == 2'b01;

assign es_st_data = sl_b ?  {4{es_rkd_value[ 7:0]}} :
                    sl_h ?  {2{es_rkd_value[15:0]}} :
                            es_rkd_value;  

assign data_sram_addr  = vaddr;
assign data_sram_en = es_mem_op != 4'b0 && es_valid && !(ws_ex || ws_ertn_flush || ms_ex || ms_ertn_flush || es_ex || es_ertn);//若后面发生中断，停止使用data_ram
assign data_sram_wdata =   {{8{data_sram_we[3]}}&es_st_data[31:24],
                            {8{data_sram_we[2]}}&es_st_data[23:16],
                            {8{data_sram_we[1]}}&es_st_data[15:8],
                            {8{data_sram_we[0]}}&es_st_data[7:0]};
assign data_sram_we = (data_sram_en & ~es_mem_op[3]) ? //inst_st_type
                        {
                            {4{sl_b}} & {vaddr[1:0] == 2'b11, vaddr[1:0] == 2'b10, vaddr[1:0] == 2'b01, vaddr[1:0] == 2'b00} |
                            {4{sl_h}} & {{2{vaddr[1]}}, {2{~vaddr[1]}}} |
                            {4{sl_w}}
                        }:
                        4'b0;

// always @(posedge clk) begin
//     if (reset) begin
//         es_req <= 1'b0;
//     end
//     else if (data_sram_addr_ok & data_sram_req) begin
//         es_req <= 1'b0;
//     end
//     else if (data_sram_en & ms_allowin) begin
//         es_req <= 1'b1;
//     end
// end
assign es_req = data_sram_en && ms_allowin;


assign data_sram_req = es_req;
assign data_sram_hand = data_sram_addr_ok & data_sram_req;
assign data_sram_wr  = (data_sram_we != 4'h0 & es_valid & !(ws_ex || ws_ertn_flush || ms_ex || ms_ertn_flush || es_ex || es_ertn));
assign data_sram_size= sl_w ? 2'd2:
                       sl_h ? 2'd1:
                               2'd0;
assign data_sram_wstrb = data_sram_we;

endmodule
